home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / Wave / WavesWorld / Source / IBPalettes / WW3DKit / WW3DShape.m < prev    next >
Encoding:
Text File  |  1995-03-22  |  58.2 KB  |  1,831 lines

  1. // copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
  2. // see COPYRIGHT for reuse legalities
  3. //
  4.  
  5. #import "WWTCLKit.h"
  6.  
  7. #import "EveCommand.h"
  8. #import "RIBCommand.h"
  9. #import "RIBTranslate.h"
  10. #import "WWEveParser.h"
  11. #import "WW3DShape.h"
  12. #import "WW3DWell.h"
  13. #import "Protocol_WWAnimatable.h"
  14. #import "WW3DAttributeState.h"
  15. #import "usefulWW3DFunctions.h"
  16.  
  17. #import "WWSceneClock.h"  // has the sceneClock protocol - formalize this!
  18.  
  19. #import <string.h>
  20.  
  21. @interface WW3DShape(Private)
  22. - updateEveCmd:(EveCommand *)eveCmd;
  23. @end
  24.  
  25. // this block was put in for Ian - wave has no idea what it does...
  26. @implementation WW3DShape(Private)
  27. static const char *RIBTranslateCmd = "Translate";
  28. static const char *RIBTranslateLimitation = "Currently require all the Translate arguments appearing in an EveCmd to be traced variables.";
  29.  
  30. - updateEveCmd:(EveCommand *)eveCmd
  31. {
  32.     WWTCLClosedCmd *tclClosedCmd;
  33.     WWTCLVarTrace *trace;
  34.     char transComp[100];
  35.     List *tracedVars;
  36.     const char *cmd;
  37.     int nVars;
  38.     
  39.     tclClosedCmd = [eveCmd command];
  40.     cmd = [tclClosedCmd cmd];
  41.     if (strstr(cmd, RIBTranslateCmd)) {
  42.     tracedVars = [tclClosedCmd tracedVars];
  43.     nVars = [tracedVars count];
  44.     if (nVars < 3) {
  45.         NXLogError(RIBTranslateLimitation);
  46.         return self;
  47.     }
  48. #if 0
  49.     trace = [tracedVars objectAt:0];
  50.     sprintf(transComp, "%f", transform[3][0]);
  51.     [interp setVar:[trace varName] toValue:transComp];
  52.     trace = [tracedVars objectAt:1];
  53.     sprintf(transComp, "%f", transform[3][1]);
  54.     [interp setVar:[trace varName] toValue:transComp];
  55.     trace = [tracedVars objectAt:2];
  56.     sprintf(transComp, "%f", transform[3][2]);
  57.     [interp setVar:[trace varName] toValue:transComp];
  58. #endif
  59.     strcpy(transComp, "0.0");
  60.     trace = [tracedVars objectAt:0];
  61.     [interp setVar:(char *)[trace varName] toValue:transComp];
  62.     trace = [tracedVars objectAt:1];
  63.     [interp setVar:(char *)[trace varName] toValue:transComp];
  64.     trace = [tracedVars objectAt:2];
  65.     [interp setVar:(char *)[trace varName] toValue:transComp];
  66.     }
  67.     return self;
  68. }
  69. @end
  70.  
  71. @implementation WW3DShape
  72.  
  73.  
  74. + initialize {  [WW3DShape setVersion:2]; return self; }
  75.  
  76. - init
  77. {
  78.   [super init];
  79.  
  80.   ribCommandList = [[List alloc] init];
  81.   childList = [[List alloc] init];
  82.   siblingList = [[List alloc] init];
  83.  
  84.   unselectedColor[0] = 0.9;  unselectedColor[1] = 0.9;  unselectedColor[2] = 0.9;
  85.  
  86.   selectedColor[0] = 0.9;  selectedColor[1] = 0.9;  selectedColor[2] = 0.0;
  87.  
  88.   xColor[0] = 1.0;  xColor[1] = 0.0;  xColor[2] = 0.0;
  89.   xExtent = 0.0;
  90.  
  91.   yColor[0] = 0.0;  yColor[1] = 1.0;  yColor[2] = 0.0;
  92.   yExtent = 0.0;
  93.  
  94.   zColor[0] = 0.0;  zColor[1] = 0.0;  zColor[2] = 1.0;
  95.   zExtent = 0.0;
  96.  
  97.   selected = NO;
  98.   drawOrigin = NO;
  99.   [self setSelectable:YES];
  100.  
  101.   shadingGroup = NULL;
  102.   materialGroup = NULL;
  103.   geometryGroup = NULL;
  104.  
  105.   pathSeparator = '/';
  106.  
  107.   dirtyBoundingBox = YES;
  108.   dirtyBases = YES;
  109.  
  110.   interp = nil;
  111.   sceneClock = nil;
  112.  
  113.   hasEveCmd = NO;
  114.   
  115.   originTesselationVector[0] = originTesselationVector[1] = 4.0;
  116.  
  117.   return self;
  118. }
  119.  
  120. - initWithInterp:newInterp andSceneClock:newSceneClock
  121. {
  122.   [self init];
  123.   interp = newInterp;
  124.   sceneClock = newSceneClock;
  125.  
  126.   return self;
  127. }
  128.  
  129. // need to put awake in here...
  130.  
  131. - awake
  132. {
  133.   [super awake];
  134.   originTesselationVector[0] = originTesselationVector[1] = 4.0;
  135. /*
  136.   NXLogError("in awake, WW3DShape <%s> boundingBox is (%f, %f) (%f, %f) (%f, %f)\n",
  137.              [self shapeName],
  138.              boundingBox[0], boundingBox[1], boundingBox[2], boundingBox[3], boundingBox[4], boundingBox[4]);
  139. */ 
  140.   return self;
  141. }
  142.  
  143. - free
  144. {
  145.   //NXLogError("WW3DShape %s (%p) %p being free'ed\n", [self shapeName], [self shapeName], self);
  146.   [[ribCommandList freeObjects] free];
  147.  
  148.   // we assume super will send the free message to our descendants...
  149.   //NXLogError("childList %p being free'ed\n", childList);
  150.   [childList free];
  151.   //NXLogError("siblingList %p being free'ed\n", siblingList);
  152.   [siblingList free];
  153.  
  154.   return [super free];
  155. }
  156.  
  157. - copyFromZone:(NXZone *)zone  
  158. {  
  159.    id   newCopy = [super copyFromZone:zone];
  160.  
  161.    NXLogError("WARNING: copyFromZone: incompletely implemented for %s", [[self class] name]);
  162.  
  163.    return newCopy;
  164. }
  165.  
  166. // this method was put in for Ian - wave has no idea what it does...
  167. - performUpdateForInteraction:(WW3DCamera *)camera
  168. /*
  169.  * For now, we hit the simplicity trail...
  170.  * We enumerate the ribCommandList; for each EveCommand
  171.  * encountered, we determine if it conforms to the WWRenderable
  172.  * protocol. If it does, and it affects the transformation
  173.  * matrix of the shape, then we go ahead and write the various
  174.  * (traced) variables for the RIBCommand.
  175.  */
  176. {
  177.     if (hasEveCmd) {
  178.     WWEveParser *eveParser;
  179.     int count, i;
  180.     
  181.     eveParser = [[camera delegate] parser];
  182.     count = [ribCommandList count];
  183.     for (i = 0; i < count; i++) {
  184.         id aCmd = [ribCommandList objectAt:i];
  185.         if ([aCmd isKindOf:[EveCommand class]]) {
  186.         [self updateEveCmd:aCmd];
  187.         }
  188.     }
  189.     }
  190.     return self;
  191. }
  192.  
  193. - tclInterp { return interp; }
  194.  
  195. - sceneClock { return sceneClock; }
  196.  
  197.  
  198. - transformCTM:(WW3DAttributeState *)attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  199. {  NXLogError("why are you calling transformCTM:startingAt:endingAt: for WW3DShape <%s>?\n", [self shapeName]);
  200.    return self;
  201. }
  202.  
  203. - (BOOL)isSelectable { return YES; } // This is a N3DKit thing, not mine
  204.  
  205. - (BOOL)isLerpable { return NO; }
  206. - lerpWith:b by:(float)uValue { return self; }
  207. - lerpSelfWith:b by:(float)uValue { return self; }
  208.  
  209. - (BOOL)isMotionBlurrable { return YES; }  // this isn't strictly true, ya know...
  210.  
  211. - (BOOL)isCompoundCommand { return YES; } // this isn't strictly true, ya know...
  212.  
  213. - (BOOL)hasBoundingBox { return YES; }  // this isn't strictly true, ya know...
  214.  
  215. - (float)lastSampleIsAt
  216. {
  217.    float  oldestSample = 0.0, newSample;
  218.    int      i, howMany = [ribCommandList count];
  219.  
  220.  
  221.   for (i = 0; i < howMany; i++)
  222.   {  newSample = [(id <WWRenderable>)[ribCommandList objectAt:i] lastSampleIsAt];
  223.      if (newSample > oldestSample)
  224.      {  oldestSample = newSample;
  225.      }
  226.   }
  227.  
  228.   newSample = [descendant lastSampleIsAt];
  229.   if (newSample > oldestSample)
  230.   {  oldestSample = newSample;
  231.   }
  232.  
  233.   newSample = [nextPeer lastSampleIsAt];
  234.   if (newSample > oldestSample)
  235.   {  oldestSample = newSample;
  236.   }
  237.  
  238.   return oldestSample;
  239. }
  240.  
  241. - (unsigned long int)maxSampleBandwidth;
  242. {
  243.    unsigned long int  maxSampleBandwidth = 50;  //WAVE FIX ME
  244.    int      i, howMany = [ribCommandList count];
  245.  
  246.  
  247.   for (i = 0; i < howMany; i++)
  248.   {  maxSampleBandwidth += [(id <WWRenderable>)[ribCommandList objectAt:i] maxSampleBandwidth];
  249.   }
  250.  
  251.   maxSampleBandwidth += [descendant maxSampleBandwidth];
  252.   maxSampleBandwidth += [nextPeer maxSampleBandwidth];
  253.  
  254.   return maxSampleBandwidth;
  255. }
  256.  
  257.  
  258. - (BOOL)isMoot
  259. {
  260.   return NO;
  261. }
  262.  
  263. - (BOOL)isMootStartingAt:(float)startTime endingAt:(float)endTime  {  return [self isMoot];  }
  264.  
  265. - (BOOL)theSameAs:otherRIBCommand
  266. {
  267.   return NO;
  268. }
  269.  
  270. - (BOOL)similarTo:otherRIBCommand 
  271. {
  272.   if ([self class] != [otherRIBCommand class])
  273.   {  return NO;
  274.   }
  275.   return YES;
  276. }
  277.  
  278.  
  279. - setShader_:newShader { return [super setShader:newShader]; }
  280.  
  281. - removeSurfaceShader:sender
  282. {
  283.   if (surfaceShader)
  284.   {  [surfaceShader free];
  285.   }
  286.   surfaceShader = nil;
  287.   return self;
  288. }
  289.  
  290. - removeDisplacementShader:sender
  291. {
  292.   if (displacementShader)
  293.   {  [displacementShader free];
  294.   }
  295.   displacementShader = nil;
  296.   return self;
  297. }
  298.  
  299.  
  300. - setBoundingBox:(RtBound *)newBoundingBox { return nil; }
  301.  
  302. // this is really a private method when some part of a shape (i.e. one
  303. // of it's rib commands) does something that it knows probably
  304. //invalidates the entire shape's bounding box.
  305. - setBoundingBoxDirty { dirtyBoundingBox = YES; return self; }
  306.  
  307.  
  308. // here's the deal.  Given a bounding box, we're going to transform that
  309. // by the given transformation matrix.  Since this effects the extent
  310. // of the object, we have to transform all 8 points of the bounding box.
  311. // After transforming them, we the redetermine the min and max of things
  312. - determineBound:(RtBound *)newBB givenBound:(RtBound *)aBB andCTM:(RtMatrix)aCTM 
  313. {
  314.    RtPoint  bboxPoints[8], bboxPointsTransformed[8];
  315.    int      i;
  316.             
  317.  
  318.    N3D_XComp(bboxPoints[0]) = (*aBB)[0];
  319.    N3D_YComp(bboxPoints[0]) = (*aBB)[2];
  320.    N3D_ZComp(bboxPoints[0]) = (*aBB)[4];
  321.  
  322.    N3D_XComp(bboxPoints[1]) = (*aBB)[1];
  323.    N3D_YComp(bboxPoints[1]) = (*aBB)[2];
  324.    N3D_ZComp(bboxPoints[1]) = (*aBB)[4];
  325.  
  326.    N3D_XComp(bboxPoints[2]) = (*aBB)[0];
  327.    N3D_YComp(bboxPoints[2]) = (*aBB)[3];
  328.    N3D_ZComp(bboxPoints[2]) = (*aBB)[4];
  329.  
  330.    N3D_XComp(bboxPoints[3]) = (*aBB)[1];
  331.    N3D_YComp(bboxPoints[3]) = (*aBB)[3];
  332.    N3D_ZComp(bboxPoints[3]) = (*aBB)[4];
  333.  
  334.    N3D_XComp(bboxPoints[4]) = (*aBB)[0];
  335.    N3D_YComp(bboxPoints[4]) = (*aBB)[2];
  336.    N3D_ZComp(bboxPoints[4]) = (*aBB)[5];
  337.  
  338.    N3D_XComp(bboxPoints[5]) = (*aBB)[1];
  339.    N3D_YComp(bboxPoints[5]) = (*aBB)[2];
  340.    N3D_ZComp(bboxPoints[5]) = (*aBB)[5];
  341.  
  342.    N3D_XComp(bboxPoints[6]) = (*aBB)[0];
  343.    N3D_YComp(bboxPoints[6]) = (*aBB)[3];
  344.    N3D_ZComp(bboxPoints[6]) = (*aBB)[5];
  345.  
  346.    N3D_XComp(bboxPoints[7]) = (*aBB)[1];
  347.    N3D_YComp(bboxPoints[7]) = (*aBB)[3];
  348.    N3D_ZComp(bboxPoints[7]) = (*aBB)[5];
  349.  
  350.    N3DMult3DPoints(bboxPoints, 8, aCTM, bboxPointsTransformed);
  351.  
  352.    // start off setting newBB to be the first point
  353.    (*newBB)[0] = (*newBB)[1] = N3D_XComp(bboxPointsTransformed[0]);
  354.    (*newBB)[2] = (*newBB)[3] = N3D_YComp(bboxPointsTransformed[0]);
  355.    (*newBB)[4] = (*newBB)[5] = N3D_ZComp(bboxPointsTransformed[0]);
  356.  
  357.    for (i = 1; i < 8; i++)
  358.    {  //// X
  359.       if ((*newBB)[0] > N3D_XComp(bboxPointsTransformed[i]))
  360.       {  (*newBB)[0] = N3D_XComp(bboxPointsTransformed[i]);
  361.       }
  362.       if ((*newBB)[1] < N3D_XComp(bboxPointsTransformed[i]))
  363.       {  (*newBB)[1] = N3D_XComp(bboxPointsTransformed[i]);
  364.       }
  365.       //// Y
  366.       if ((*newBB)[2] > N3D_YComp(bboxPointsTransformed[i]))
  367.       {  (*newBB)[2] = N3D_YComp(bboxPointsTransformed[i]);
  368.       }
  369.       if ((*newBB)[3] < N3D_YComp(bboxPointsTransformed[i]))
  370.       {  (*newBB)[3] = N3D_YComp(bboxPointsTransformed[i]);
  371.       }
  372.       //// Z
  373.       if ((*newBB)[4] > N3D_ZComp(bboxPointsTransformed[i]))
  374.       {  (*newBB)[4] = N3D_ZComp(bboxPointsTransformed[i]);
  375.       }
  376.       if ((*newBB)[5] < N3D_ZComp(bboxPointsTransformed[i]))
  377.       {  (*newBB)[5] = N3D_ZComp(bboxPointsTransformed[i]);
  378.       }
  379.    }
  380.  
  381.    return self;
  382. }
  383.  
  384.  
  385. - (BOOL)pushesOrPopsCTM { return NO; }
  386. - (BOOL)pushesCTM { return NO; }
  387. - (BOOL)popsCTM { return NO; }
  388.  
  389. // the asumption here is that cmd (i - 1) pushes the CTM, so we want to 
  390. // grovel over the remaining commands until we find one that pops the CTM
  391. // it should recurse 
  392. // the starting condition 
  393. - (int)findBoundingBoxFromRIBCommands:(int)startingIndex using:attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  394. {
  395.   RtBound   tmpBoundingBox;
  396.   RtMatrix  tmpCTM;
  397.   int       i = startingIndex,
  398.             incr,
  399.             howMany = [ribCommandList count];
  400.   id        cmd;
  401.   id        newAttributeState;
  402.  
  403.  
  404.   // first we need to find a bounding box to start with
  405.   while (i < howMany)
  406.   {  cmd = [ribCommandList objectAt:i];
  407.  
  408.      // if the cmd pops the attribute stack, we're done.
  409.      // we return how many commands we've gone through in our time here
  410.      if ([cmd popsCTM])
  411.      {  i++;
  412.         return (i - startingIndex);
  413.      }
  414.  
  415.      if ([cmd pushesCTM])  // here we go again...
  416.      {  newAttributeState = [[WW3DAttributeState alloc] init];
  417.         i++;
  418.         incr = [self findBoundingBoxFromRIBCommands:i using:newAttributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  419.         // we now need to merge this sub-attribute block into our current one
  420.         if ([newAttributeState hasBoundingBox])
  421.         {  // This means that the bounding box in newAttributeState needs to be
  422.            // transformed by the ctm of attributeState, and then  we need to 
  423.            // "grow" to the current bound with that tranformed sub-bound.
  424.  
  425.            [attributeState getTransformMatrix:tmpCTM];
  426.           WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, [newAttributeState boundingBox], tmpCTM);
  427.           [attributeState growBoundingBox:&tmpBoundingBox];
  428.        }
  429.        else
  430.        {  //NXLogError("warning: WW3DShape <%s> has a moot set of commands at indices %d to %d\n", [self shapeName], (i - 1), (i + incr - 1));
  431.           //NXLogError("\tif there was a LightSource or AreaLight source in there, you can ignore this warning, though...\n");
  432.        }
  433.        i += incr;
  434.      }
  435.      else 
  436.      {  [cmd transformCTM:attributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  437.         i++;
  438.      }
  439.  
  440.     if ([cmd hasBoundingBox])
  441.     {  // great! we grow the bound of the current attribute state
  442.        [attributeState growBoundingBox:[cmd boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]];
  443.     }
  444.  
  445.   }
  446.   return (i - startingIndex);
  447. }
  448.  
  449.  
  450.  
  451. - calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  452. {
  453.   RtBound   tmpBoundingBox;
  454.   RtMatrix  childMatrix, tmpCTM;
  455.   int       i, startingChildIndex = 0, howManyKids;
  456.   id        child, kids, newAttributeState;
  457.  
  458.  
  459.   //NXLogError("calculating bbox for shape <%s>\n", [self shapeName]);
  460.  
  461.   // this one is tricky.  we'll need to be smart about this...
  462.  
  463.   // basically, each RIBCommand has it's own bounding box.  We ask
  464.   // each one in turn for it's boundingBox, and use that to build up ours.
  465.   // Remember that each command is smart enough to know if any of it's
  466.   // parameters have changed, but we need to be smart to realize if we have
  467.   // rotated/translated/scaled.  Recall that for scaling and translating we
  468.   // can just add that to the boundingBox without asking everyone to recalculate,
  469.   // but rotating mucks up everything.  
  470.  
  471.   // so we need to transform these points against our 4x4, right?
  472.   // Does the 3DKit take care of that for us?  Hmmm...
  473.  
  474.   // Actually, I don't think we need to cat our 4x4 against it, 
  475.   // although I'm still unclear...  Anyway, what we really want to do is 
  476.   // grovel over our ribCommandList and, to each object which answers YES
  477.   // to hasBoundingBox we send the appropriate msg to get the bounding box
  478.   // and compare it against ours.
  479.  
  480.   // actually, one more difficulty is that even though we might not
  481.   // have any geometry (i.e. have any RIBCommands that respond 
  482.   // positively to -hasBoundingBox), we still might have some commands
  483.   // transform the boundingBox, ie. do more than concatenate an identity 
  484.   // matrix against the matrix passed into -transform:.
  485.  
  486.   // what if you had 3 commands, the first two of which didn't have bounding boxes.
  487.   // first time through, i == 1 at the end
  488.   // next time, i == 2 at the end
  489.   // final time through, i doesn't get incremented, but we are done
  490.   // actually, it's more complicated than that.
  491.   // some of these RIBCommands might not have a bounding box, but they do transform the CTM
  492.   // also, you might have some transformational commands, then some geometry, then more
  493.   // transformational commands, then geometry, then some more transformational... These
  494.   // would all effect the children shapes in toto, but would also be affecting the 
  495.   // bounding box of the various geometry commands.
  496.   // urgh...
  497.  
  498.   // It's not really so bad.  Everything is taking place in the space of this shape, which 
  499.   // means that this shape's transformation matrix isn't consulted.  We start off with 
  500.   // the identity matrix, and transform a tmp CTM (current transformation matrix) as we
  501.   // move through the commands.
  502.  
  503.   // Actually, it's a bit trickier than you might think.  The problem
  504.   // is that although we have no fear of dropping down into a child until
  505.   // we get to the list of kids, we just might hit a RIBAttributeBegin,
  506.   // RIBTransformBegin, or RIBSolidBegin object, which would have the same effect
  507.   // So, it looks like we need to extend the WWRenderable protocol to let us know
  508.   // if the object pushes or pops the transformation stack.  We'll need to recurse
  509.   // down the transformation tree, building up the CTM, and transforming any bounding
  510.   // box we find.  We start off when the command pushes the stack.  We cons up a new
  511.   // CTM and set it to the Identity matrix.  We then start walking down the commands,
  512.   // first asking the command if it has a boundingBox.  If it does, we set our bound
  513.   // to it, and then transform the points in the bound by our CTM.  We
  514.   // then continue walking down the tree, asking each command if has a
  515.   // bounding box, and if does, transforming that box by the CTM, and then
  516.   // comparing it to our current bound, and growing it accordingly.  If the
  517.   // command doesn't have a bound, we just transform our CTM and our
  518.   // current bound by it.  We continue this until we get find a command
  519.   // which pops the attribute stack.  If, along the way, we hit a command which
  520.   // actually pushes the stack, we need to recurse down and call this routine again.
  521.   // The routine should be passed, and return, the id of a list of WW3DAttributeState
  522.   // objects, which have a bounding box, a CTM, etc. in them.
  523.  
  524.   newAttributeState = [[WW3DAttributeState alloc] init];
  525.   i = [self findBoundingBoxFromRIBCommands:0 using:newAttributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  526.   if (i != [ribCommandList count])
  527.   {  NXLogError("warning: shape <%s> has an unbalanced attribute delimiter at command %d (expected %d)\n", 
  528.                 [self shapeName], i, [ribCommandList count]);
  529.      return nil;
  530.   }
  531.  
  532.   [newAttributeState getTransformMatrix:tmpCTM];
  533.  
  534.   if (![newAttributeState hasBoundingBox])  // this Shape has no real geometry in it, but it might have kids...
  535.   {  // we still need some approximation to start off with for this shape's bounding box.
  536.      // to get it, we use our first child's bounding box.
  537.      // if we don't have any kids, we just return.
  538.      kids = [self children];
  539.      if ([kids count] < 1)
  540.      {  dirtyBoundingBox = NO;
  541.         return self;
  542.      }
  543.  
  544.      // note that it's fair to assume that if we have children, each one has a bounding
  545.      // box.  If it didn't have a bounding box, that would mean it was a leaf node of
  546.      // the tree without any geometry, which means we've got a moot node.
  547.      child = [kids objectAt:0];
  548.  
  549.      // now, we can't just copy this bounding box, because it needs to
  550.      // be transformed by the 4x4 that represents the transformation from this
  551.      // shape to the child, i.e. it's 4x4.  
  552.      N3D_CopyBound(*([child boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]), tmpBoundingBox);
  553.  
  554.      WW3DDetermineBoundGivenBoundAndCTM(&boundingBox, &tmpBoundingBox, tmpCTM);
  555.  
  556.      startingChildIndex = 1;
  557.   }
  558.   else
  559.   {  N3D_CopyBound(*([newAttributeState boundingBox]), boundingBox);
  560.   }
  561.  
  562.   // at this point, we only have a bounding box for the geometry
  563.   // associated with this shape's geometry.  We now need to recurse down
  564.   // our children, asking each to calculate it's bounding box.  We then
  565.   // compare that bounding box with our own, and update ours accordingly.
  566.   // note that if we didn't have any geometry in this shape, we've already
  567.   // looked at the boundingBox of our first child, and set our boundingBox
  568.   // to that, so startingChildIndex has been incremented to 1, so we
  569.   // don't waste time redoing that.
  570.   kids = [self children];
  571.   howManyKids = [kids count];
  572.   for (i = startingChildIndex; i < howManyKids; i++)
  573.   {  child = [kids objectAt:i];
  574.      N3D_CopyBound(*([child boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]), tmpBoundingBox);
  575.  
  576.      // the boundingBox that we get from the child should already be transformed 
  577.      // correctly, right?  Or do we also need to concat the child's transform
  578.      // onto it?  Yes, I think we do need to.  We get the bound, convert it to points,
  579.      // transform it against the child's CTM, then multiply *that* by the
  580.      // tmpCTM (which takes into account transformational RIBCommands in this
  581.      // shape, which will get executed before the child shape, and then
  582.      // convert those points back to a bound.  We can check that against 
  583.      // the current bound.
  584.  
  585.      // we need to transform that bounding box to reflect the transformational 
  586.      // commands that precede it in this shape's ribCommandList
  587.      [child getTransformMatrix:childMatrix];
  588.      WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, &tmpBoundingBox, childMatrix);
  589.      WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, &tmpBoundingBox, tmpCTM);
  590.  
  591.      // now we should all be in the same space; compare against current boundingBox
  592.      //// X
  593.      if (tmpBoundingBox[0] < boundingBox[0])
  594.      {  boundingBox[0] = tmpBoundingBox[0];
  595.      }
  596.      if (tmpBoundingBox[1] > boundingBox[1])
  597.      {  boundingBox[1] = tmpBoundingBox[1];
  598.      }
  599.      //// Y
  600.      if (tmpBoundingBox[2] < boundingBox[2])
  601.      {  boundingBox[2] = tmpBoundingBox[2];
  602.      }
  603.      if (tmpBoundingBox[3] > boundingBox[3])
  604.      {  boundingBox[3] = tmpBoundingBox[3];
  605.      }
  606.      //// Z
  607.      if (tmpBoundingBox[4] < boundingBox[4])
  608.      {  boundingBox[4] = tmpBoundingBox[4];
  609.      }
  610.      if (tmpBoundingBox[5] > boundingBox[5])
  611.      {  boundingBox[5] = tmpBoundingBox[5];
  612.      }
  613.   }
  614.  
  615.   xExtent = boundingBox[1] - boundingBox[0];
  616.   yExtent = boundingBox[3] - boundingBox[2];
  617.   zExtent = boundingBox[5] - boundingBox[4];
  618.  
  619.   //fprintf(stderr, "shape %s has extents of (%f, %f, %f)\n", [self shapeName], xExtent, yExtent, zExtent);
  620.   //fprintf(stderr, "\t bbox : X (%f, %f)\n", boundingBox[0], boundingBox[1]);
  621.   //fprintf(stderr, "\t      : Y (%f, %f)\n", boundingBox[2], boundingBox[3]);
  622.   //fprintf(stderr, "\t      : Z (%f, %f)\n", boundingBox[4], boundingBox[5]);
  623.  
  624.   dirtyBoundingBox = NO;
  625.  
  626.   return self;
  627. }
  628.  
  629. #define X_AXIS 1.0, 0.0, 0.0
  630. #define Y_AXIS 0.0, 1.0, 0.0
  631. #define Z_AXIS 0.0, 0.0, 1.0
  632. #define    wwMax(a,b)  (((a)>(b))?(a):(b))
  633. #define    wwMin(a,b)  (((a)>(b))?(b):(a))
  634.  
  635. - drawOriginStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  636. {  if (drawOrigin)
  637.    {  [self boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime];  // call this to make sure that my boundingBox is current...
  638.       RiAttributeBegin();
  639.       RiGeometricApproximation(RI_TESSELATION, RI_PARAMETRIC, originTesselationVector, RI_NULL);
  640.  
  641.       // draw along Z axis
  642.       RiColor(zColor);
  643.       if (wwMin(xExtent, yExtent) == 0.0)
  644.       {  if ((boundingBox[4] < 0.0) && (boundingBox[5] < 0.0))
  645.          {  RiCylinder((0.01 * (wwMax(xExtent, yExtent))), 
  646.                boundingBox[4], 0.0, 
  647.                360.0, RI_NULL);
  648.      }
  649.          else
  650.          {  RiCylinder((0.01 * (wwMax(xExtent, yExtent))), 
  651.                wwMin(0.0, boundingBox[4]), boundingBox[5], 
  652.                360.0, RI_NULL);
  653.      }
  654.       }
  655.       else
  656.       {  if ((boundingBox[4] < 0.0) && (boundingBox[5] < 0.0))
  657.          {  RiCylinder((0.01 * (wwMin(xExtent, yExtent))), 
  658.                boundingBox[4], 0.0, 
  659.                360.0, RI_NULL);
  660.       }
  661.          else
  662.          {  RiCylinder((0.01 * (wwMin(xExtent, yExtent))), 
  663.                wwMin(0.0, boundingBox[4]), boundingBox[5], 
  664.                360.0, RI_NULL);
  665.       }
  666.       }
  667.  
  668.       // rotate about X -90 degrees to be pointing along the Y axis
  669.       RiRotate(-90, X_AXIS);
  670.       RiColor(yColor);
  671.       if (wwMin(xExtent, zExtent) == 0.0)
  672.       {  if ((boundingBox[2] < 0.0) && (boundingBox[3] < 0.0)) 
  673.          {  RiCylinder((0.01 * (wwMax(xExtent, zExtent))), 
  674.                boundingBox[2], 0.0,
  675.                360.0, RI_NULL);
  676.      }
  677.          else
  678.          {  RiCylinder((0.01 * (wwMax(xExtent, zExtent))), 
  679.                wwMin(0.0, boundingBox[2]), boundingBox[3], 
  680.                360.0, RI_NULL);
  681.          }
  682.       }
  683.       else
  684.       {  if ((boundingBox[2] < 0.0) && (boundingBox[3] < 0.0)) 
  685.          {  RiCylinder((0.01 * (wwMin(xExtent, zExtent))), 
  686.                boundingBox[2], 0.0,
  687.                360.0, RI_NULL);
  688.      }
  689.          else
  690.      {  RiCylinder((0.01 * (wwMin(xExtent, zExtent))), 
  691.                wwMin(0.0, boundingBox[2]), boundingBox[3], 
  692.                360.0, RI_NULL);
  693.      }
  694.       }
  695.  
  696.       // rotate about Y 90 degrees to be pointing along the X axis
  697.       RiRotate(90, Y_AXIS);
  698.       RiColor(xColor);
  699.       if (wwMin(yExtent, zExtent) == 0.0)
  700.       {  if ((boundingBox[0] < 0.0) && (boundingBox[1] < 0.0))
  701.          {   RiCylinder((0.01 * (wwMax(yExtent, zExtent))), 
  702.             boundingBox[0], 0.0,
  703.             360.0, RI_NULL);
  704.      }
  705.      else
  706.          {   RiCylinder((0.01 * (wwMax(yExtent, zExtent))), 
  707.             wwMin(0.0, boundingBox[0]), boundingBox[1], 
  708.             360.0, RI_NULL);
  709.      }
  710.       }
  711.       else
  712.       {  if ((boundingBox[0] < 0.0) && (boundingBox[1] < 0.0))
  713.      {   RiCylinder((0.01 * (wwMin(yExtent, zExtent))), 
  714.             boundingBox[0], 0.0,
  715.             360.0, RI_NULL);
  716.      }
  717.          else
  718.      {   RiCylinder((0.01 * (wwMin(yExtent, zExtent))), 
  719.             wwMin(0.0, boundingBox[0]), boundingBox[1], 
  720.             360.0, RI_NULL);
  721.      }
  722.       }
  723.       RiAttributeEnd();
  724.    }
  725.    if (selected) 
  726.    {  RiColor(selectedColor);
  727.    }
  728.    return self;
  729. }
  730.  
  731.  
  732. - renderMaps:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime usingStream:(NXStream *)ns
  733. {
  734.   int      i, howMany = [ribCommandList count];
  735.  
  736.  
  737.   for (i = 0; i < howMany; i++)
  738.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  739.   }
  740.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  741.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  742.  
  743.   return self;
  744. }
  745.  
  746.  
  747. - renderMaps:(WW3DCamera *)camera usingStream:(NXStream *)ns
  748. {
  749.   int      i, howMany = [ribCommandList count];
  750.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  751.            shutterCloseTime = [camera shutterCloseTime];
  752.  
  753.  
  754.   for (i = 0; i < howMany; i++)
  755.   {  [(id <WWAnimatable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  756.   }
  757.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  758.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  759.  
  760.   return self;
  761. }
  762.  
  763.  
  764.  
  765. - renderMaps:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  766. {
  767.   int      i, howMany = [ribCommandList count];
  768.  
  769.  
  770.   for (i = 0; i < howMany; i++)
  771.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  772.   }
  773.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  774.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  775.  
  776.   return self;
  777. }
  778.  
  779.  
  780. - renderMaps:(WW3DCamera *)camera
  781. {
  782.   int      i, howMany = [ribCommandList count];
  783.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  784.            shutterCloseTime = [camera shutterCloseTime];
  785.  
  786.  
  787.   for (i = 0; i < howMany; i++)
  788.   {  [(id <WWAnimatable>)[ribCommandList objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  789.   }
  790.   [descendant renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  791.   [nextPeer renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  792.  
  793.   return self;
  794. }
  795.  
  796.  
  797.  
  798. - renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  799. {
  800.   int                i, howMany = [ribCommandList count];
  801.   id <WWRenderable>  obj;
  802.  
  803.  
  804.   if ([self doesDrawAsBox])
  805.   {  return self;
  806.   }
  807.  
  808.   // for the origin, we want cylinders the length of the bounding box
  809.   // I could see wanting to do this only on-screen, but sometimes for real...
  810.   if (NXDrawingStatus == NX_DRAWING) 
  811.   {  [self drawOriginStartingAt:shutterOpenTime endingAt:shutterCloseTime];
  812.      for (i = 0; i < howMany; i++)
  813.      {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  814.      }
  815.   }
  816.   else
  817.   {  for (i = 0; i < howMany; i++) // do the moot check
  818.      {  obj = (id <WWRenderable>)[ribCommandList objectAt:i];
  819.         if (![obj isMoot])
  820.         {  [obj renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  821.         }
  822.      }
  823.   }
  824.   return self;
  825. }
  826.  
  827.  
  828. - renderSelf:(WW3DCamera *)camera
  829. {
  830.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  831.            shutterCloseTime = [camera shutterCloseTime];
  832.  
  833.  
  834.   return [self renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  835. }
  836.  
  837. // I don't like the way NeXT defines this; should recurse down
  838. - setDrawAsBox:(BOOL)flag
  839. {
  840.   int  i, howMany;
  841.   id   kids;
  842.  
  843.  
  844.   [super setDrawAsBox:flag];
  845.   kids = [self children];
  846.   howMany = [kids count];
  847.   for (i = 0; i < howMany; i++)
  848.   {  [[kids objectAt:i] setDrawAsBox:flag];
  849.   }
  850.  
  851.   return self;
  852. }
  853.  
  854. - renderSelfAsBox:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  855. {
  856.   int      i, howMany = [ribCommandList count];
  857.   RtInt    nPolys = 6;
  858.   RtInt    nVerts[6] = {4, 4, 4, 4, 4, 4 };
  859.   RtInt    verts[24] = {3, 2, 6, 7, 
  860.                         2, 1, 5, 6,
  861.                         1, 0, 4, 5,
  862.                         0, 3, 7, 4,
  863.                         7, 6, 5, 4,
  864.                         0, 1, 2, 3};
  865.   RtPoint  thePointsOfACube[8];
  866.   RtBound  bb;
  867.  
  868.  
  869.   if (NXDrawingStatus == NX_DRAWING) 
  870.   {  [self drawOriginStartingAt:shutterOpenTime endingAt:shutterCloseTime];
  871.   }
  872.  
  873.   N3D_CopyBound(*([self boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]), bb);
  874.   thePointsOfACube[0][0] = bb[0];
  875.   thePointsOfACube[0][1] = bb[3];
  876.   thePointsOfACube[0][2] = bb[5];
  877.  
  878.   thePointsOfACube[1][0] = bb[1];
  879.   thePointsOfACube[1][1] = bb[3];
  880.   thePointsOfACube[1][2] = bb[5];
  881.  
  882.   thePointsOfACube[2][0] = bb[1];
  883.   thePointsOfACube[2][1] = bb[3];
  884.   thePointsOfACube[2][2] = bb[4];
  885.  
  886.   thePointsOfACube[3][0] = bb[0];
  887.   thePointsOfACube[3][1] = bb[3];
  888.   thePointsOfACube[3][2] = bb[4];
  889.  
  890.   thePointsOfACube[4][0] = bb[0];
  891.   thePointsOfACube[4][1] = bb[2];
  892.   thePointsOfACube[4][2] = bb[5];
  893.  
  894.   thePointsOfACube[5][0] = bb[1];
  895.   thePointsOfACube[5][1] = bb[2];
  896.   thePointsOfACube[5][2] = bb[5];
  897.  
  898.   thePointsOfACube[6][0] = bb[1];
  899.   thePointsOfACube[6][1] = bb[2];
  900.   thePointsOfACube[6][2] = bb[4];
  901.  
  902.   thePointsOfACube[7][0] = bb[0];
  903.   thePointsOfACube[7][1] = bb[2];
  904.   thePointsOfACube[7][2] = bb[4];
  905.  
  906.   RiPointsPolygons(nPolys, nVerts, verts, RI_P, thePointsOfACube, RI_NULL);
  907.  
  908.   // we now need to render our rib commands.
  909.   // we need to do this so that our children shapes are transformed correctly
  910.   // this is actually a bit weird, because the rib commands 
  911.  
  912.   for (i = 0; i < howMany; i++)
  913.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] renderSelfAsBox:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  914.   }
  915.  
  916.   return self;
  917. }
  918.  
  919. - renderSelfAsBox:(N3DCamera *)camera
  920. {
  921.   RtFloat  shutterOpenTime = [(WW3DCamera *)camera shutterOpenTime],
  922.            shutterCloseTime = [(WW3DCamera *)camera shutterCloseTime];
  923.  
  924.  
  925.   return [self renderSelfAsBox:(WW3DCamera *)camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  926. }
  927.  
  928.  
  929. - preRenderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  930. {
  931.   int      i, howMany = [ribCommandList count];
  932.  
  933.  
  934.   for (i = 0; i < howMany; i++)
  935.   {  [(id <WWRenderable>)[ribCommandList objectAt:i] preRenderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  936.   }
  937.   return self;
  938. }
  939.  
  940.  
  941. - preRenderSelf:(WW3DCamera *)camera
  942. {
  943.   int  i, howMany = [ribCommandList count];
  944.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  945.            shutterCloseTime = [camera shutterCloseTime];
  946.  
  947.  
  948.   for (i = 0; i < howMany; i++)
  949.   {  [(id <WWAnimatable>)[ribCommandList objectAt:i] preRenderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  950.   }
  951.   return self;
  952. }
  953.  
  954.  
  955. - setShadingGroup:(const char *)newGroup
  956. {
  957.   if (shadingGroup) { free(shadingGroup); }
  958.   shadingGroup = NXCopyStringBuffer(newGroup);
  959.   return self;
  960. }
  961.  
  962. - setMaterialGroup:(const char *)newGroup
  963. {
  964.   if (materialGroup) { free(materialGroup); }
  965.   materialGroup = NXCopyStringBuffer(newGroup);
  966.   return self;
  967. }
  968.  
  969. - setGeometryGroup:(const char *)newGroup
  970. {
  971.   if (geometryGroup) { free(geometryGroup); }
  972.   geometryGroup = NXCopyStringBuffer(newGroup);
  973.   return self;
  974. }
  975.  
  976.  
  977. - ribCommands { return ribCommandList; }
  978. - appendRIBCommand:newRIBCommand
  979. {
  980.     if ([newRIBCommand isKindOf:[EveCommand class]]) {
  981.     hasEveCmd = YES;
  982.     }
  983.     [ribCommandList addObject:newRIBCommand];
  984.     dirtyBoundingBox = YES;
  985.     return self;
  986. }
  987.  
  988. - setSelected:(BOOL)selectionFlag andDrawOrigin:(BOOL)drawOriginFlag 
  989. {  selected = selectionFlag; 
  990.    [self setDrawOrigin:drawOriginFlag];
  991.    return self; 
  992. }
  993.  
  994. - setDrawOrigin:(BOOL)drawOriginFlag  {  drawOrigin = drawOriginFlag; return self; }
  995.  
  996. - updateBases
  997. {
  998.   int   howManyPositions, i;
  999.   BOOL  foundIt;
  1000.   id    cmd = nil;   
  1001.  
  1002.  
  1003.   howManyPositions = [ribCommandList count];
  1004.   i = howManyPositions - 1;
  1005.   foundIt = NO;
  1006.   while (!foundIt && (i > 0))
  1007.   {  cmd = [ribCommandList objectAt:i];
  1008.      if ([cmd respondsTo:@selector(uBasis)])
  1009.      {  foundIt = YES;
  1010.      }
  1011.      else
  1012.      {  i--;
  1013.      }
  1014.   }
  1015.  
  1016.   if (foundIt) // that's it; copy it from the last RIBBasis cmd
  1017.   {  N3D_CopyMatrix(*([cmd uBasis]), uBasis);
  1018.      uBasisToken = [ancestor uBasisToken];
  1019.      uStep = [cmd uStep];
  1020.      N3D_CopyMatrix(*([cmd vBasis]), vBasis);
  1021.      vBasisToken = [ancestor vBasisToken];
  1022.      vStep = [cmd vStep];
  1023.   }
  1024.   else  // not defined here; ask up the shape hierarchy
  1025.   {  if ((ancestor) && (ancestor != self)) // get it from our ancestor
  1026.      {  N3D_CopyMatrix(*([ancestor uBasis]), uBasis);
  1027.         uBasisToken = [ancestor uBasisToken];
  1028.         uStep = [ancestor uStep];
  1029.         N3D_CopyMatrix(*([ancestor vBasis]), vBasis);
  1030.         vBasisToken = [ancestor vBasisToken];
  1031.         vStep = [ancestor vStep];
  1032.      }
  1033.      else // we are the root; fill in bezier bases for both u and v
  1034.      {  N3D_CopyMatrix(RiBezierBasis, uBasis);
  1035.         uBasisToken = RI_BEZIER;
  1036.         uStep = 3;
  1037.         N3D_CopyMatrix(RiBezierBasis, vBasis);
  1038.         vStep = 3;
  1039.         vBasisToken = RI_BEZIER;
  1040.      }
  1041.   }
  1042.  
  1043.   dirtyBases = NO;
  1044.   return self;
  1045. }
  1046.  
  1047.  
  1048. - (RtBound *)boundingBoxStartingAt:(RtFloat)intervalStart endingAt:(RtFloat)intervalEnd
  1049.    if (!dirtyBoundingBox)
  1050.    {  // okay, this means we haven't been sent any messages that would have invalidated the boundingBox cache
  1051.       // now check intervals...
  1052.       if ((intervalStart == bbIntervalStart) && (intervalEnd == bbIntervalEnd))
  1053.       {  // cool! we can use the cached version!!
  1054.          return &boundingBox; 
  1055.       }
  1056.    }
  1057.    // damn! have to recalculate it..
  1058.    [self calculateBoundingBoxStartingAt:intervalStart endingAt:intervalEnd];
  1059.    // don't forget to refill the cache
  1060.    bbIntervalStart = intervalStart;
  1061.    bbIntervalEnd = intervalEnd;
  1062.    dirtyBoundingBox = FALSE;
  1063.  
  1064.    // so what things might invalidate the cache?  Basically if I get a
  1065.    // new child or an addition to my ribCommandList, the bounding box
  1066.    // is invalidated.
  1067.  
  1068.    return &boundingBox; 
  1069. }
  1070.  
  1071. - (RtBasis *)uBasis {  if (dirtyBases) {  [self updateBases]; }   return &uBasis; }
  1072. - (RtToken)uBasisToken {  if (dirtyBases) {  [self updateBases]; }  return uBasisToken; }
  1073. - (RtInt)uStep {  if (dirtyBases) {  [self updateBases]; }  return uStep; }
  1074. - (RtBasis *)vBasis {  if (dirtyBases) {  [self updateBases]; }  return &vBasis; }
  1075. - (RtToken)vBasisToken {  if (dirtyBases) {  [self updateBases]; }  return vBasisToken; }
  1076. - (RtInt)vStep {  if (dirtyBases) {  [self updateBases]; }   return vStep; }
  1077.  
  1078. - setTransformMatrix:(RtMatrix)tm
  1079. {
  1080.    //[self logTransformWithMsg:"setTransformMatrix:(RtMatrix)tm BEFORE"];
  1081.    [super setTransformMatrix:tm];
  1082.    //[self logTransformWithMsg:"setTransformMatrix:(RtMatrix)tm AFTER"];
  1083.    return self;
  1084. }
  1085.  
  1086. - concatTransformMatrix:(RtMatrix)ctm premultiply:(BOOL)flag
  1087. {
  1088.    //[self logTransformWithMsg:"concatTransformMatrix:(RtMatrix)ctm premultiply:(BOOL)flag BEFORE"];
  1089.    [super concatTransformMatrix:ctm premultiply:flag];
  1090.    //[self logTransformWithMsg:"concatTransformMatrix:(RtMatrix)ctm premultiply:(BOOL)flag AFTER"];
  1091.    return self;
  1092. }
  1093.  
  1094. - rotateAngle:(float)ang axis:(RtPoint)anAxis
  1095. {
  1096.    //[self logTransformWithMsg:"rotateAngle:(float)ang axis:(RtPoint)anAxis BEFORE"];
  1097.    [super rotateAngle:ang axis:anAxis];
  1098.    //[self logTransformWithMsg:"rotateAngle:(float)ang axis:(RtPoint)anAxis AFTER"];
  1099.    return self;
  1100. }
  1101.  
  1102. - preRotateAngle:(float)ang axis:(RtPoint)anAxis
  1103. {
  1104.    //[self logTransformWithMsg:"preRotateAngle:(float)ang axis:(RtPoint)anAxis BEFORE"];
  1105.    [super preRotateAngle:ang axis:anAxis];
  1106.    //[self logTransformWithMsg:"preRotateAngle:(float)ang axis:(RtPoint)anAxis AFTER"];
  1107.    return self;
  1108. }
  1109.  
  1110. - scale:(float)sx :(float)sy :(float)sz
  1111. {
  1112.    //[self logTransformWithMsg:"scale:(float)sx :(float)sy :(float)sz BEFORE"];
  1113.    [super scale:sx :sy :sz];
  1114.    //[self logTransformWithMsg:"scale:(float)sx :(float)sy :(float)sz AFTER"];
  1115.    return self;
  1116. }
  1117.  
  1118. - preScale:(float)sx :(float)sy :(float)sz
  1119. {
  1120.    //[self logTransformWithMsg:"preScale:(float)sx :(float)sy :(float)sz BEFORE"];
  1121.    [super preScale:sx :sy :sz];
  1122.    //[self logTransformWithMsg:"preScale:(float)sx :(float)sy :(float)sz AFTER"];
  1123.    return self;
  1124. }
  1125.  
  1126. - scaleUniformly:(float)s
  1127. {
  1128.    //[self logTransformWithMsg:"scaleUniformly:(float)s BEFORE"];
  1129.    [super scaleUniformly:s];
  1130.    //[self logTransformWithMsg:"scaleUniformly:(float)s AFTER"];
  1131.    return self;
  1132. }
  1133.  
  1134. - preScaleUniformly:(float)s
  1135. {
  1136.    //[self logTransformWithMsg:"preScaleUniformly:(float)s BEFORE"];
  1137.    [super preScaleUniformly:s];
  1138.    //[self logTransformWithMsg:"preScaleUniformly:(float)s AFTER"];
  1139.    return self;
  1140. }
  1141.  
  1142. - translate:(float)tx :(float)ty :(float)tz
  1143. {
  1144.    //[self logTransformWithMsg:"translate:(float)tx :(float)ty :(float)tz BEFORE"];
  1145.    [super translate:tx :ty :tz];
  1146.    //[self logTransformWithMsg:"translate:(float)tx :(float)ty :(float)tz AFTER"];
  1147.    return self;
  1148. }
  1149.  
  1150. - preTranslate:(float)tx :(float)ty :(float)tz
  1151. {
  1152.    //[self logTransformWithMsg:"preTranslate:(float)tx :(float)ty :(float)tz BEFORE"];
  1153.    [super preTranslate:tx :ty :tz];
  1154.    //[self logTransformWithMsg:"preTranslate:(float)tx :(float)ty :(float)tz AFTER"];
  1155.    return self;
  1156. }
  1157.  
  1158.  
  1159. - logTransformWithMsg:(const char *)aMsg
  1160. {
  1161.    if (aMsg)
  1162.    {  NXLogError("WW3DShape %s transform matrix: %s\n", [self shapeName], aMsg);
  1163.    }
  1164.    else
  1165.    {  NXLogError("WW3DShape %s transform matrix\n", [self shapeName]);
  1166.    }
  1167.    NXLogError("\t%f\t%f\t%f\t%f\n", transform[0][0], transform[0][1], transform[0][2], transform[0][3]); 
  1168.    NXLogError("\t%f\t%f\t%f\t%f\n", transform[1][0], transform[1][1], transform[1][2], transform[1][3]); 
  1169.    NXLogError("\t%f\t%f\t%f\t%f\n", transform[2][0], transform[2][1], transform[2][2], transform[2][3]); 
  1170.    NXLogError("\t%f\t%f\t%f\t%f\n", transform[3][0], transform[3][1], transform[3][2], transform[3][3]); 
  1171.    return self;
  1172. }
  1173.  
  1174. - addChild:newChild
  1175. {
  1176.   id  oldDescendant = nil,
  1177.       oldDescendantList = [[List alloc] init],
  1178.       parentShape = self;
  1179.  
  1180.  
  1181.   // I think I want to unlink the old descendant, link the child in as
  1182.   // the new one, and then make the oldDescendant the peer of the child.
  1183.   oldDescendant = [parentShape descendant];
  1184.   if (oldDescendant)
  1185.   {  while (oldDescendant) 
  1186.      {  [oldDescendantList addObject:oldDescendant];
  1187.         [oldDescendant unlink];
  1188.         oldDescendant = [parentShape descendant];
  1189.      }
  1190.   }
  1191.   [parentShape linkDescendant:newChild];
  1192.   if (oldDescendantList)
  1193.   {  oldDescendant = [oldDescendantList objectAt:0];
  1194.      while (oldDescendant) 
  1195.      {  [newChild linkPeer:oldDescendant];
  1196.         [oldDescendantList removeObjectAt:0];
  1197.         oldDescendant = [oldDescendantList objectAt:0];
  1198.      }
  1199.      [oldDescendantList free];
  1200.   }
  1201.   dirtyBoundingBox = YES;
  1202.  
  1203.   return self;
  1204. }
  1205.  
  1206.  
  1207. - siblings
  1208.   id  currentPeer;
  1209.  
  1210.  
  1211.   // Really should cache this by trapping all the hierarchy frobbing and
  1212.   // having a dirty bit set.  For right now, recalculate each time.
  1213.  
  1214.   [siblingList empty];
  1215.  
  1216.   if (self != [self firstPeer])
  1217.   {  [siblingList addObject:[self firstPeer]];
  1218.      currentPeer = [self firstPeer];
  1219.  
  1220.      while ([currentPeer nextPeer])
  1221.      {  [siblingList addObject:[currentPeer nextPeer]];
  1222.         currentPeer = [currentPeer nextPeer];
  1223.      }
  1224.   }
  1225.  
  1226.   return siblingList;
  1227. }
  1228.  
  1229. - children 
  1230.   id  currentPeer;
  1231.  
  1232.  
  1233.   // Really should cache this by trapping all the hierarchy frobbing and
  1234.   // having a dirty bit set.  For right now, recalculate each time.
  1235.  
  1236.   [childList empty];
  1237.  
  1238.   [childList addObject:[self descendant]];
  1239.   currentPeer = descendant;
  1240.   // note that the descendant is the firstPeer of it's siblings...
  1241.   while ([currentPeer nextPeer])
  1242.   {  [childList addObject:[currentPeer nextPeer]];
  1243.      currentPeer = [currentPeer nextPeer];
  1244.   }
  1245.  
  1246.   return childList;
  1247. }
  1248.  
  1249. - parent { return ancestor; }
  1250.  
  1251. - (unsigned short)pathSeparator { return pathSeparator; }
  1252. - setPathSeparator:(unsigned short)newSeparator { pathSeparator = newSeparator; return self; }
  1253.  
  1254.  
  1255. - getChildGivenPath:(const char *)path
  1256. {
  1257.   char  *part = (char *)path;
  1258.   int   cnt, howMany, i;
  1259.   id    ret;
  1260.  
  1261.  
  1262.   // the string looks something like "/foo/bar/zap/zow"
  1263.   // if the whole string is "/", return yourself
  1264.   // if the first part doesn't match your name, return nil.
  1265.   // if what's left after the first part is nil, return self;
  1266.   // if you're still here, send the message to each child until it doesn't return nil.  
  1267.   // if you get through all your children and it still returns nil, return nil.
  1268.  
  1269.   if ((strlen(path) == 1) && ((unsigned short)(*part) == pathSeparator))
  1270.   {  return self;
  1271.   }
  1272.  
  1273.   while (*part && ((unsigned short)(*part) != pathSeparator)) { part++; } 
  1274.   // from "/foo/bar" to "/bar" or from "/foo" to ""
  1275.   cnt = part - (path + 1);
  1276.   if (!cnt) {  return nil; } // catch the "/foo" to "" case
  1277.  
  1278.   if (!strncmp((path+1), [self shapeName], cnt))
  1279.   {  // the first part is my name
  1280.      if (!*part) { return self; }// if there isn't anything else, I'm who they're looking for
  1281.  
  1282.      // otherwise, send message to each child to see if it's them
  1283.      howMany = [childList count];
  1284.      i = 0;
  1285.      while (i < howMany)
  1286.      {  ret = [[childList objectAt:i] getChildGivenPath:part];
  1287.         if (ret)
  1288.         {  return ret;
  1289.         }
  1290.         i++;
  1291.      }
  1292.   }
  1293.   // that isn't my name at the beginning - this isn't for me or any of my kids
  1294.   return nil;
  1295. }
  1296.  
  1297. - (char *)getPath
  1298. {
  1299.   char  *path, pathSeparatorString[2]; 
  1300.   int   cnt = 1, 
  1301.         i;
  1302.   id    theShape;
  1303.   List  *myShapeList;
  1304.  
  1305.  
  1306.   myShapeList = [[List alloc] init];
  1307.   theShape = self;
  1308.   while (theShape)
  1309.   {  cnt += 2 + strlen([theShape shapeName]);
  1310.      [myShapeList addObject:theShape];
  1311.      theShape = [theShape ancestor];
  1312.   }
  1313.  
  1314.   path = malloc(cnt);
  1315.   if (!path)
  1316.   {  [myShapeList free];
  1317.      return NULL;
  1318.   }
  1319.  
  1320.   path[0] = '\0';
  1321.   sprintf(pathSeparatorString, "%c", pathSeparator);
  1322.   for (i = [myShapeList count] - 1; i >= 0; i--)  
  1323.   {  strcat(path, pathSeparatorString);
  1324.      strcat(path, [[myShapeList objectAt:i] shapeName]);
  1325.   }
  1326.   
  1327.   return path;
  1328. }
  1329.  
  1330. - getInitialTransformMatrix:(RtMatrix)aMatrix
  1331. {
  1332.   N3D_CopyMatrix(N3DIdentityMatrix, aMatrix);
  1333.   return self;
  1334. }
  1335.  
  1336. - setSelectedColor:(RtColor)newColor
  1337. {  selectedColor[0] = newColor[0];
  1338.    selectedColor[1] = newColor[1];
  1339.    selectedColor[2] = newColor[2];
  1340.  
  1341.    return self;
  1342. }
  1343. - (RtColor *)selectedColor { return &selectedColor; }
  1344.  
  1345. - setUnselectedColor:(RtColor)newColor
  1346. {  unselectedColor[0] = newColor[0];
  1347.    unselectedColor[1] = newColor[1];
  1348.    unselectedColor[2] = newColor[2];
  1349.  
  1350.    return self;
  1351. }
  1352. - (RtColor *)unselectedColor { return &unselectedColor; }
  1353.  
  1354.  
  1355. // WavesWorld archiving:
  1356. // writeEve:(NXStream *)stream
  1357. // writeScene:(NXStream *)stream
  1358.  
  1359. - (BOOL)matricesAreEqual:(RtMatrix)m1 :(RtMatrix)m2
  1360. {
  1361.   if (m1[0][0] != m2[0][0])  {  return NO; }
  1362.   if (m1[0][1] != m2[0][1])  {  return NO; }
  1363.   if (m1[0][2] != m2[0][2])  {  return NO; }
  1364.   if (m1[0][3] != m2[0][3])  {  return NO; }
  1365.  
  1366.   if (m1[1][0] != m2[1][0])  {  return NO; }
  1367.   if (m1[1][1] != m2[1][1])  {  return NO; }
  1368.   if (m1[1][2] != m2[1][2])  {  return NO; }
  1369.   if (m1[1][3] != m2[1][3])  {  return NO; }
  1370.  
  1371.   if (m1[2][0] != m2[2][0])  {  return NO; }
  1372.   if (m1[2][1] != m2[2][1])  {  return NO; }
  1373.   if (m1[2][2] != m2[2][2])  {  return NO; }
  1374.   if (m1[2][3] != m2[2][3])  {  return NO; }
  1375.  
  1376.   if (m1[3][0] != m2[3][0])  {  return NO; }
  1377.   if (m1[3][1] != m2[3][1])  {  return NO; }
  1378.   if (m1[3][2] != m2[3][2])  {  return NO; }
  1379.   if (m1[3][3] != m2[3][3])  {  return NO; }
  1380.  
  1381.   return YES;
  1382. }
  1383.  
  1384. - (BOOL)isIdentityMatrix:(RtMatrix)m  { return [self matricesAreEqual:(RtFloat (*)[4])N3DIdentityMatrix :m]; }
  1385.  
  1386. - writeEve:(NXStream *)stream atTabLevel:(int)tab
  1387. {
  1388.    RtMatrix  aMatrix;
  1389.    int       i, howManySpaces, howMany = [ribCommandList count];
  1390.    id        aCmd;
  1391.  
  1392.  
  1393.    for (i = 0; i < tab; i++)
  1394.    {  NXPrintf(stream, "\t");
  1395.    }
  1396.    NXPrintf(stream, "startShape {%s} ", [self shapeName]); 
  1397.    [self getTransformMatrix:aMatrix];
  1398.    if ([self isIdentityMatrix:aMatrix])
  1399.    {  NXPrintf(stream, ";\n");
  1400.    }
  1401.    else  // need to add the transformation matrix, since it's not the identity matrix...
  1402.    {  NXPrintf(stream, "{");
  1403.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1404.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1405.       // it would be nice if we put these on separate lines, but line up...
  1406.       // how far in do we need to go? well, first we need to tab the right amount, and then
  1407.       // move over "startShape " + strlen([self shapeName]) + "{"....
  1408.       for (i = 0; i < tab; i++)
  1409.       {  NXPrintf(stream, "\t");
  1410.       }
  1411.       howManySpaces = strlen("startShape ") + strlen([self shapeName]) + strlen(" {");
  1412.  
  1413.       for (i = 0; i < howManySpaces; i++)
  1414.       {  NXPrintf(stream, " ");
  1415.       }
  1416.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1417.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1418.    
  1419.       for (i = 0; i < tab; i++)
  1420.       {  NXPrintf(stream, "\t");
  1421.       }
  1422.       for (i = 0; i < howManySpaces; i++)
  1423.       {  NXPrintf(stream, " ");
  1424.       }
  1425.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1426.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1427.  
  1428.       for (i = 0; i < tab; i++)
  1429.       {  NXPrintf(stream, "\t");
  1430.       }
  1431.       for (i = 0; i < howManySpaces; i++)
  1432.       {  NXPrintf(stream, " ");
  1433.       }
  1434.       NXPrintf(stream, "%f %f %f %f};\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1435.       NXPrintf(stream, "\n");
  1436.    }
  1437.    // WAVE!!! Need to write out the rest of my shaders here!!!
  1438.    if (surfaceShader)
  1439.    {  [surfaceShader writeEve:stream atTabLevel:(tab + 1)];
  1440.       NXPrintf(stream, "\n");
  1441.    }
  1442.    if (displacementShader)
  1443.    {  [displacementShader writeEve:stream atTabLevel:(tab + 1)];
  1444.       NXPrintf(stream, "\n");
  1445.    }
  1446.  
  1447.    // okay, now tell all my rib commands to write themselves out...
  1448.    for (i = 0; i < howMany; i++)
  1449.    {  aCmd = [ribCommandList objectAt:i];
  1450.       [aCmd writeEve:stream atTabLevel:(tab+1)];
  1451.       NXPrintf(stream, "\n");
  1452.    }
  1453.  
  1454.    // now tell my descendant to writeEve: itself...
  1455.    if (descendant) // don't put an extra new-line out if I have no descendant...
  1456.    {  [descendant writeEve:stream atTabLevel:(tab + 1)];
  1457.       NXPrintf(stream, "\n");
  1458.    }
  1459.  
  1460.    for (i = 0; i < tab; i++)
  1461.    {  NXPrintf(stream, "\t");
  1462.    }
  1463.    NXPrintf(stream, "endShape\n"); 
  1464.  
  1465.    // now tell my nextPeer to writeEve: itself...
  1466.    [nextPeer writeEve:stream atTabLevel:tab];
  1467.  
  1468.    return self;
  1469. }
  1470.  
  1471. - writeScene:(NXStream *)stream atTabLevel:(int)tab
  1472. {
  1473.    RtMatrix  aMatrix;
  1474.    int       i, howManySpaces, howMany = [ribCommandList count];
  1475.    id        aCmd;
  1476.  
  1477.  
  1478.    for (i = 0; i < tab; i++)
  1479.    {  NXPrintf(stream, "\t");
  1480.    }
  1481.    NXPrintf(stream, "startShape {%s} ", [self shapeName]); 
  1482.    [self getTransformMatrix:aMatrix];
  1483.    if ([self isIdentityMatrix:aMatrix])
  1484.    {  NXPrintf(stream, ";\n");
  1485.    }
  1486.    else  // need to add the transformation matrix, since it's not the identity matrix...
  1487.    {  NXPrintf(stream, "{");
  1488.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1489.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1490.       // it would be nice if we put these on separate lines, but line up...
  1491.       // how far in do we need to go? well, first we need to tab the right amount, and then
  1492.       // move over "startShape " + strlen([self shapeName]) + "{"....
  1493.       for (i = 0; i < tab; i++)
  1494.       {  NXPrintf(stream, "\t");
  1495.       }
  1496.       howManySpaces = strlen("startShape ") + strlen([self shapeName]) + strlen(" {");
  1497.  
  1498.       for (i = 0; i < howManySpaces; i++)
  1499.       {  NXPrintf(stream, " ");
  1500.       }
  1501.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1502.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1503.    
  1504.       for (i = 0; i < tab; i++)
  1505.       {  NXPrintf(stream, "\t");
  1506.       }
  1507.       for (i = 0; i < howManySpaces; i++)
  1508.       {  NXPrintf(stream, " ");
  1509.       }
  1510.       NXPrintf(stream, "%f %f %f %f ",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1511.       NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1512.  
  1513.       for (i = 0; i < tab; i++)
  1514.       {  NXPrintf(stream, "\t");
  1515.       }
  1516.       for (i = 0; i < howManySpaces; i++)
  1517.       {  NXPrintf(stream, " ");
  1518.       }
  1519.       NXPrintf(stream, "%f %f %f %f};\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1520.       NXPrintf(stream, "\n");
  1521.    }
  1522.    // WAVE!!! Need to write out the rest of my shaders here!!!
  1523.    if (surfaceShader)
  1524.    {  [surfaceShader writeScene:stream atTabLevel:(tab + 1)];
  1525.       NXPrintf(stream, "\n");
  1526.    }
  1527.    if (displacementShader)
  1528.    {  [displacementShader writeScene:stream atTabLevel:(tab + 1)];
  1529.       NXPrintf(stream, "\n");
  1530.    }
  1531.  
  1532.    // okay, now tell all my rib commands to write themselves out...
  1533.    for (i = 0; i < howMany; i++)
  1534.    {  aCmd = [ribCommandList objectAt:i];
  1535.       [aCmd writeScene:stream atTabLevel:(tab+1)];
  1536.       NXPrintf(stream, "\n");
  1537.    }
  1538.  
  1539.    // now tell my descendant to writeScene: itself...
  1540.    if (descendant) // don't put an extra new-line out if I have no descendant...
  1541.    {  [descendant writeScene:stream atTabLevel:(tab + 1)];
  1542.       NXPrintf(stream, "\n");
  1543.    }
  1544.  
  1545.    for (i = 0; i < tab; i++)
  1546.    {  NXPrintf(stream, "\t");
  1547.    }
  1548.    NXPrintf(stream, "endShape\n"); 
  1549.  
  1550.    // now tell my nextPeer to writeScene: itself...
  1551.    [nextPeer writeScene:stream atTabLevel:tab];
  1552.  
  1553.    return self;
  1554. }
  1555.  
  1556.  
  1557. - write3DTextScene:(NXStream *)stream atTabLevel:(int)tab index:(int)index time:(float)time until:(float)lastTime
  1558. {
  1559.    RtMatrix  aMatrix;
  1560.    int       i, 
  1561.              howManySpaces, howMany = [ribCommandList count];
  1562.    id        aCmd;
  1563.  
  1564.  
  1565.    for (i = 0; i < tab; i++)
  1566.    {  NXPrintf(stream, "\t");
  1567.    }
  1568.  
  1569.    NXPrintf(stream, "set __text__(colorWW3DShape) {1 1 1}\n");
  1570.  
  1571.    NXPrintf(stream, "startShape WW3DShape; EveCmd {Color $__text__(colorWW3DShape)}; ");
  1572.    // need tab
  1573.    // need index (position in current list)
  1574.    NXPrintf(stream, 
  1575.         "EveCmd {Translate [expr { %d * $__text__(tabLength)}] [expr {$__text__(spacingFactor) * %d * $__text__(spacing) * $__text__(fontSize)}] 0 };\n", 
  1576.             tab, index); 
  1577.    for (i = 0; i < tab; i++)
  1578.    {  NXPrintf(stream, "\t");
  1579.    }
  1580.    NXPrintf(stream, "  EveCmd {WW3DText $__text__(fontName) $__text__(fontSize) {");
  1581.      NXPrintf(stream, "startShape %s ", [self shapeName]); 
  1582.      [self getTransformMatrix:aMatrix];
  1583.      if ([self isIdentityMatrix:aMatrix])
  1584.      {  NXPrintf(stream, " } left;\n");
  1585.      }
  1586.      else  // need to add the transformation matrix, since it's not the identity matrix...
  1587.      {  NXPrintf(stream, "{");
  1588.         NXPrintf(stream, "%f %f %f %f ",  aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1589.         NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1590.         // it would be nice if we put these on separate lines, but line up...
  1591.         // how far in do we need to go? well, first we need to tab the right amount, and then
  1592.         // move over "startShape " + strlen([self shapeName]) + "{"....
  1593.         for (i = 0; i < tab; i++)
  1594.         {  NXPrintf(stream, "\t");
  1595.         }
  1596.         howManySpaces = strlen("startShape ") + strlen([self shapeName]) + strlen(" {");
  1597.   
  1598.         for (i = 0; i < howManySpaces; i++)
  1599.         {  NXPrintf(stream, " ");
  1600.         }
  1601.         NXPrintf(stream, "%f %f %f %f ",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1602.         NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1603.  
  1604.         for (i = 0; i < tab; i++)
  1605.         {  NXPrintf(stream, "\t");
  1606.         }
  1607.         for (i = 0; i < howManySpaces; i++)
  1608.         {  NXPrintf(stream, " ");
  1609.         }
  1610.         NXPrintf(stream, "%f %f %f %f ",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1611.         NXPrintf(stream, "\\\n"); // this is so we can have a new line that tcl won't see...
  1612.   
  1613.         for (i = 0; i < tab; i++)
  1614.         {  NXPrintf(stream, "\t");
  1615.         }
  1616.         for (i = 0; i < howManySpaces; i++)
  1617.         {  NXPrintf(stream, " ");
  1618.         }
  1619.         NXPrintf(stream, "%f %f %f %f}} left;\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1620.         NXPrintf(stream, "\n");
  1621.      }
  1622.    NXPrintf(stream, "}\n");
  1623.   
  1624.    index++;
  1625.    // okay, next object - it will be a child of this shape:
  1626.    if (surfaceShader)
  1627.    {  [surfaceShader write3DTextScene:stream atTabLevel:(tab+1) index:index++ time:time until:lastTime];
  1628.       NXPrintf(stream, "\n");
  1629.    }
  1630.  
  1631.    // okay, now tell all my rib commands to write themselves out...
  1632.    for (i = 0; i < howMany; i++)
  1633.    {  aCmd = [ribCommandList objectAt:i];
  1634.       [aCmd write3DTextScene:stream atTabLevel:(tab+1) index:index++ time:time until:lastTime];
  1635.       NXPrintf(stream, "\n");
  1636.    }
  1637.  
  1638.    // now tell my descendant to writeEve: itself...
  1639.    if (descendant) // don't put an extra new-line out if I have no descendant...
  1640.    {  [descendant write3DTextScene:stream atTabLevel:(tab+1) index:index++ time:time until:lastTime];
  1641.       NXPrintf(stream, "\n");
  1642.    }
  1643.  
  1644.    // okay, now finish this text shape off by putting out the endShape
  1645.  
  1646.    for (i = 0; i < tab; i++)
  1647.    {  NXPrintf(stream, "\t");
  1648.    }
  1649.    NXPrintf(stream, "startShape endShape; ");
  1650.    // need tab
  1651.    // need index (position in current list)
  1652.    NXPrintf(stream, 
  1653.         "EveCmd {Translate [expr { %d * $__text__(tabLength)}] [expr {$__text__(spacingFactor) * %d * $__text__(spacing) * $__text__(fontSize)}] 0 };\n", 
  1654.             tab, index); 
  1655.    NXPrintf(stream, "  EveCmd {WW3DText $__text__(fontName) $__text__(fontSize) {");
  1656.      NXPrintf(stream, "endShape");
  1657.    NXPrintf(stream, "} left;}\n");
  1658.  
  1659.    for (i = 0; i < tab; i++)
  1660.    {  NXPrintf(stream, "\t");
  1661.    }
  1662.   NXPrintf(stream, "endShape;\n");
  1663.  
  1664.    for (i = 0; i < tab; i++)
  1665.    {  NXPrintf(stream, "\t");
  1666.    }
  1667.   NXPrintf(stream, "endShape;\n");  // okay, end off the WW3DShape
  1668.  
  1669.    // now tell my nextPeer to writeEve: itself...
  1670.    [nextPeer write3DTextScene:stream atTabLevel:(tab+1) index:index time:time until:lastTime];
  1671.  
  1672.    return self;
  1673. }
  1674.  
  1675.  
  1676. - writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
  1677. {
  1678.    RtMatrix  aMatrix;
  1679.    int       i, newTab, howMany = [ribCommandList count];
  1680.    id        aCmd;
  1681.  
  1682.  
  1683.    for (i = 0; i < tab; i++)
  1684.    {  NXPrintf(stream, "\t");
  1685.    }
  1686.    NXPrintf(stream, "# startShape {%s}\n", [self shapeName]);
  1687.    for (i = 0; i < tab; i++)
  1688.    {  NXPrintf(stream, "\t");
  1689.    }
  1690.    NXPrintf(stream, "DEF \"%s\" Separator {\n", [self shapeName]);
  1691.    [self getTransformMatrix:aMatrix];
  1692.    if (![self isIdentityMatrix:aMatrix])
  1693.    {  
  1694.  
  1695.       for (i = 0; i < (tab+1); i++)
  1696.       {  NXPrintf(stream, "\t");
  1697.       }
  1698.       NXPrintf(stream, "MatrixTransform {\n");
  1699.       for (i = 0; i < (tab+2); i++)
  1700.       {  NXPrintf(stream, "\t");
  1701.       }
  1702.       NXPrintf(stream, "matrix %f %f %f %f\n", aMatrix[0][0], aMatrix[0][1], aMatrix[0][2], aMatrix[0][3]);
  1703.       for (i = 0; i < (tab+2); i++)
  1704.       {  NXPrintf(stream, "\t");
  1705.       }
  1706.       NXPrintf(stream, "       %f %f %f %f\n",  aMatrix[1][0], aMatrix[1][1], aMatrix[1][2], aMatrix[1][3]);
  1707.       for (i = 0; i < (tab+2); i++)
  1708.       {  NXPrintf(stream, "\t");
  1709.       }
  1710.       NXPrintf(stream, "       %f %f %f %f\n",  aMatrix[2][0], aMatrix[2][1], aMatrix[2][2], aMatrix[2][3]);
  1711.       for (i = 0; i < (tab+2); i++)
  1712.       {  NXPrintf(stream, "\t");
  1713.       }
  1714.       NXPrintf(stream, "       %f %f %f %f\n", aMatrix[3][0], aMatrix[3][1], aMatrix[3][2], aMatrix[3][3]);
  1715.       for (i = 0; i < (tab+1); i++)
  1716.       {  NXPrintf(stream, "\t");
  1717.       }
  1718.       NXPrintf(stream, "}\n");
  1719.    }
  1720.    // WAVE!!! Need to write out the rest of my shaders here!!!
  1721.    if (surfaceShader)
  1722.    {  [surfaceShader writeInventorAtTime:currentTime to:stream atTabLevel:(tab + 1)];
  1723.       NXPrintf(stream, "\n");
  1724.    }
  1725.    if (displacementShader)
  1726.    {  [displacementShader writeInventorAtTime:currentTime to:stream atTabLevel:(tab + 1)];
  1727.       NXPrintf(stream, "\n");
  1728.    }
  1729.  
  1730.    // okay, now tell all my rib commands to write themselves out...
  1731.    newTab = tab + 1;
  1732.    for (i = 0; i < howMany; i++)
  1733.    {  aCmd = [ribCommandList objectAt:i];
  1734.       if (![aCmd isMootStartingAt:currentTime endingAt:currentTime])
  1735.       {  if ([aCmd popsCTM])
  1736.          {  newTab--;
  1737.          }
  1738.          [aCmd writeInventorAtTime:currentTime to:stream atTabLevel:newTab];
  1739.          if ([aCmd pushesCTM])
  1740.          {  newTab++;
  1741.          }
  1742.          NXPrintf(stream, "\n");
  1743.       }
  1744.    }
  1745.  
  1746.    // now tell my descendant to writeInventor: itself...
  1747.    if (descendant) // don't put an extra new-line out if I have no descendant...
  1748.    {  [descendant writeInventorAtTime:currentTime to:stream atTabLevel:(tab + 1)];
  1749.       NXPrintf(stream, "\n");
  1750.    }
  1751.  
  1752.    for (i = 0; i < tab; i++)
  1753.    {  NXPrintf(stream, "\t");
  1754.    }
  1755.    NXPrintf(stream, "}\n"); 
  1756.  
  1757.    // now tell my nextPeer to writeInventor: itself...
  1758.    [nextPeer writeInventorAtTime:currentTime to:stream atTabLevel:tab];
  1759.  
  1760.    return self;
  1761. }
  1762.  
  1763.  
  1764. // NeXTSTEP archiving:
  1765. #define typeVectorVersion1 "@S***[3f][3f][3f]f[3f]f[3f]f@"
  1766. #define typeValuesVersion1 &ribCommandList, &pathSeparator, \
  1767.                    &shadingGroup, &materialGroup, &geometryGroup, \
  1768.                    &selectedColor, &unselectedColor, \
  1769.                    &xColor, &xExtent, \
  1770.                    &yColor, &yExtent, \
  1771.                    &zColor, &zExtent, \
  1772.                    &interp
  1773.  
  1774. #define typeVector "@S***fff@@"
  1775. #define typeValues &ribCommandList, &pathSeparator, \
  1776.                    &shadingGroup, &materialGroup, &geometryGroup, \
  1777.                    &xExtent, &yExtent, &zExtent, \
  1778.                    &interp, &sceneClock
  1779.  
  1780.  
  1781. - read:(NXTypedStream *)stream 
  1782. {
  1783.     int    version;
  1784.     
  1785.     [super read:stream];
  1786.     
  1787.     NX_DURING
  1788.     version = NXTypedStreamClassVersion(stream, "WW3DShape");
  1789.     if (version == 0) NXReadTypes(stream, "i", &version), version = 1;
  1790.     if (version == 1) {
  1791.         NXReadTypes(stream, typeVectorVersion1, typeValuesVersion1);
  1792.         sceneClock = nil;
  1793.         NXLogError("yikes! no sceneClock was archived with this shape...");
  1794.     }
  1795.     if (version == 2) {
  1796.         NXReadTypes(stream, typeVector, typeValues);
  1797.         NXReadArray(stream, "f", 3, selectedColor);
  1798.         NXReadArray(stream, "f", 3, unselectedColor);
  1799.         NXReadArray(stream, "f", 3, xColor);
  1800.         NXReadArray(stream, "f", 3, yColor);
  1801.         NXReadArray(stream, "f", 3, zColor);
  1802.     }
  1803.     NX_HANDLER
  1804.     NXLogError("in read: %s, exception [%d] raised.\n", [[self class] name], NXLocalHandler.code);
  1805.     return nil;
  1806.     NX_ENDHANDLER
  1807.     
  1808.     return self;
  1809. }
  1810.  
  1811.  
  1812. - write:(NXTypedStream *)stream 
  1813. {
  1814.     [super write:stream];
  1815.     NXWriteTypes(stream, typeVector, typeValues);
  1816.     NXWriteArray(stream, "f", 3, selectedColor);
  1817.     NXWriteArray(stream, "f", 3, unselectedColor);
  1818.     NXWriteArray(stream, "f", 3, xColor);
  1819.     NXWriteArray(stream, "f", 3, yColor);
  1820.     NXWriteArray(stream, "f", 3, zColor);
  1821.     return self;
  1822. }
  1823.  
  1824. // boy, this is dumb... This is to get around the stupid warnings from the compiler - ask wave for details
  1825. - class { return [super class]; }
  1826.  
  1827. @end
  1828.